use termion::color;

use crate::ast::{
  ASTBinaryExpression, ASTBooleanExpression, ASTExpression, ASTNumberExpression,
  ASTParenthesizedExpression, ASTStatement, ASTUnaryExpression, ASTVisitor,
};
use crate::token::TextSpan;
use crate::{LEVEL_INDENT, LEVEL_MARK};

pub struct ASTPrinter {
  pub indent: usize,
  pub result: String,
}

impl ASTPrinter {
  // const NUMBER_COLOR: color::Rgb = color::Rgb(0, 255, 255);
  const NUMBER_COLOR: color::Green = color::Green;
  const TEXT_COLOR: color::White = color::White;
  const BRACKET_COLOR: color::LightWhite = color::LightWhite;
  const BOOLEAN_COLOR: color::Yellow = color::Yellow;

  pub fn new() -> Self {
    Self {
      indent: 0,
      result: String::new(),
    }
  }

  fn add_whitespace(&mut self) {
    // self.result.push_str(" ");
    self.result.push(' ');
  }

  fn add_padding(&mut self) {
    for _ in 0..self.indent {
      self.result.push_str("  ");
    }
  }

  fn add_boolean(&mut self, boolean: bool) {
    self
      .result
      .push_str(&format!("{}{}", Self::BOOLEAN_COLOR.fg_str(), boolean,));
  }

  fn print_with_indent(&mut self, text: &str) {
    println!("{}{}", " ".repeat(self.indent), text);
  }
}

impl ASTVisitor for ASTPrinter {
  fn visit_number_expression(&mut self, number: &ASTNumberExpression) {
    if LEVEL_MARK {
      self.print_with_indent(&format!("Number: {:#?}", number.number));
    } else {
      self
        .result
        .push_str(&format!("{}{}", Self::NUMBER_COLOR.fg_str(), number.number));
    }
  }

  fn visit_statement(&mut self, statement: &ASTStatement) {
    if LEVEL_MARK {
      self.print_with_indent("Statement:");
      self.indent += LEVEL_INDENT;
      Self::do_visit_statement(self, statement);
      self.indent -= LEVEL_INDENT;
    } else {
      self.add_padding();
      Self::do_visit_statement(self, statement);
      self
        .result
        .push_str(&format!("{}\n", color::Fg(color::Reset)));
    }
  }

  fn visit_expression(&mut self, expression: &ASTExpression) {
    if LEVEL_MARK {
      self.print_with_indent("Expression:");
      self.indent += LEVEL_INDENT;
      Self::do_visit_expression(self, expression);
      self.indent -= LEVEL_INDENT;
    } else {
      self.do_visit_expression(expression);
    }
  }

  fn visit_binary_expression(&mut self, binary_expression: &ASTBinaryExpression) {
    if LEVEL_MARK {
      self.print_with_indent("Binary Expression:");
      self.indent += LEVEL_INDENT;
      self.print_with_indent(&format!("Operator: {:?}", binary_expression.operator.kind));
      self.visit_expression(&binary_expression.lhs);
      self.visit_expression(&binary_expression.rhs);
      self.indent -= LEVEL_INDENT;
    } else {
      self.visit_expression(&binary_expression.lhs);
      self.add_whitespace();
      self.result.push_str(&format!(
        "{}{}",
        Self::TEXT_COLOR.fg_str(),
        binary_expression.operator.token.span.literal
      ));
      self.add_whitespace();
      self.visit_expression(&binary_expression.rhs);
    }
  }

  fn visit_parenthesized_expression(
    &mut self,
    parenthesized_expression: &ASTParenthesizedExpression,
  ) {
    if LEVEL_MARK {
      self.print_with_indent("Parenthesized Expression:");
      self.indent += LEVEL_INDENT;
      self.visit_expression(&parenthesized_expression.expression);
      self.indent -= LEVEL_INDENT;
    } else {
      self
        .result
        .push_str(&format!("{}{}", Self::BRACKET_COLOR.fg_str(), "("));
      self.visit_expression(&parenthesized_expression.expression);
      self
        .result
        .push_str(&format!("{}{}", Self::BRACKET_COLOR.fg_str(), ")"));
    }
  }

  fn visit_unary_expression(&mut self, unary_expression: &ASTUnaryExpression) {
    if LEVEL_MARK {
      self.print_with_indent("Unary Expression:");
      self.indent += LEVEL_INDENT;
      self.print_with_indent(&format!("Operator: {:?}", unary_expression.operator.kind));
      self.visit_expression(&unary_expression.operand);
      self.indent -= LEVEL_INDENT;
    } else {
      self.result.push_str(&format!(
        "{}{}",
        Self::TEXT_COLOR.fg_str(),
        unary_expression.operator.token.span.literal
      ));
      self.visit_expression(&unary_expression.operand);
    }
  }

  fn visit_boolean_expression(&mut self, boolean_expression: &ASTBooleanExpression) {
    if LEVEL_MARK {
      self.print_with_indent(&format!("Boolean: {:#?}", boolean_expression.value));
    } else {
      self.add_boolean(boolean_expression.value);
    }
  }

  fn visit_error(&mut self, span: &TextSpan) {
    if LEVEL_MARK {
      self.print_with_indent(&format!("Error: {:?}", span));
    } else {
      self
        .result
        .push_str(&format!("{}{}", Self::TEXT_COLOR.fg_str(), span.literal));
    }
  }
}
